package org.example.advic;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//通知类

//通知类一定是作为springbean存在
@Component
@Aspect
public class MyAdvice {
    /*

    关于切点表达式

    配置在接口上，所有实现类都会被切面影响
    execution(void org.example.dao.UserService.update())

    配置在实现类上，只是针对具体的这个实现类，会被切面影响
    execution(void org.example.dao.impl.UserServiceImpl.update())


    * 代表一个组件
    .. 代表N个组件

        返回值写法
            具体类型
            * 表示任意返回值类型
            不存在..

        包结构的写法
            具体包结构
            * 代表一层包
            .. 代表N层包
                aaa.bbb.*.ccc.ddd
                    表示aaa.bbb.xxx.ccc.ddd
                aaa.bbb..ccc.ddd
                    表示aaa.bbb.xx.xxx.x.xx.ccc.ddd

        方法名的写法
            具体的方法名
            名称前缀+*  指的是以这个指定前缀开头的任意方法名
                get*
                    getById
                    getAll

        形参的写法
            * 代表一个参数
            .. 代表任意参数

    匹配org.example包下的任意包中的UserService类或接口中所有get开头的带有一个参数的方法
    execution（public * org.example.*.UserService.get*(*))

    匹配org包下的任意包中的UserService类或接口中所有名称为getById的方法
    execution（public User org..UserService.getById(..))


 */
    //设置切点
/*    @Pointcut("execution(void org.example.service.UserService.update())")
    public void pt(){}*/

    //创建通知方法
    //前置通知 在业务执行前增强
/*    @Before("pt()")
    public void m1(){
        System.out.println("前置增强");
    }*/

/*    @After("pt()")
    public void m2(){
        System.out.println("后置增强");
    }*/

    //返回后通知
/*    @AfterReturning("pt()")
    public void m3(){
        System.out.println("返回后增强");
    }*/
/*    @AfterThrowing("pt()")
    public void m4(){
        System.out.println("异常后通知");

    }*/


    //获取切点数据 JoinPoint -------------------------------------------------------------------------

/*    @Pointcut("execution(void org.example.service.UserService.update1(..))")
    public void pt(){}*/

    //前置通知 在业务执行前增强
 /*   @Before("pt()")
    public void m1(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println("前置增强,取得业务参数"+Arrays.toString(args));

    }*/

/*    @After("pt()")
    public void m2(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println("后置增强,取得业务参数"+Arrays.toString(args));
    }*/



//    @Pointcut("execution(* org.example.service.UserService.update2())")
//    public void pt(){}

    //返回后通知returning表示返回值通过s1来接收
 /*   @AfterReturning(value = "pt()",returning = "s1")
    public void m3(String s1){
        System.out.println("返回后增强,取得返回值"+s1);
    }*/
    //异常后通知
//    @AfterThrowing(value = "pt()",throwing = "t")
//    public void m4(Throwable t){
//        System.out.println(t);
//        System.out.println("异常后通知,接收到的异常信息"+t);
//
//    }


    //环绕通知--------------
    //以后用的多，能做的事也多
    //核心业务的调用时机，由环绕通知方法来决定
    /*
    环绕通知与同时添加前置与后置的区别就在于
    核心业务的调用时机，由环绕通知方法来决定
     */
    @Pointcut("execution(* org.example.service.UserService.update2())")
   public void pt(){}
    @Around("pt()")
    public Object m1(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知开始");
        //调用核心业务
        Object result = pjp.proceed();
        Signature signature = pjp.getSignature();
        System.out.println("1--"+signature.getDeclaringTypeName());
        System.out.println("2---"+signature.getName());

        System.out.println("环绕通知结束");
        return result;
    }

    //实战应用 测试接口万次执行效率-----------------------------------------------------
/*    @Around("execution(* org.example.service.UserService.get*(..))")
    public void m1(ProceedingJoinPoint pjp) throws Throwable {
        //取方法名
        Signature signature = pjp.getSignature();
        String methodName = signature.getName();
        //时间戳
        long start = System.currentTimeMillis();
        //执行10万次业务
        for (int i = 0; i < 100000; i++) {
            //执行核心业务
            pjp.proceed();
        }
        //结束时间戳
        long end = System.currentTimeMillis();
        System.out.println(methodName+"经过10万次消耗时间"+(end-start));


    }*/

}
